/* Gluten Library -- Timer
 *
 * Provides an assortment of timers.
 *
 * $AUTHOR$    res
 * $UPDATE$    r35
 */

/* LICENSES ********************************************************************

   Copyright (c) 2013- Reuben E. Smith

   Permission is hereby granted, free of charge, to any person obtaining a copy
   of this software and associated documentation files (the "Software"), to deal
   in the Software without restriction, including without limitation the rights
   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   copies of the Software, and to permit persons to whom the Software is
   furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included in
   all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   THE SOFTWARE.

 *******************************************************************************/


// res: Might move get*Time out of Utility to here. Will probably refactor from
//      header-only to header-and-source units when we do that.


#ifndef GLUTEN_TIMER_HPP
#define GLUTEN_TIMER_HPP

#include <algorithm>
#include <limits>
#include "Utility.hpp"


namespace Gluten
{
   // res: The assumption is that someone using a timer wants to minimize the
   //      amount of time the timer itself takes, so to cut out method calls,
   //      Timers here are structs with default public access.


   //! \struct Timer
   //! \brief Gives the time (ms) between the call to start and stop.
   struct Timer
   {
      uintmax_t mark;
      uintmax_t time;


      //! \brief Default constructor.
      Timer() :
         mark(0),
         time(0)
      {
      }


      //! \brief Mark the start of the timed period.
      inline void start()
      {
         mark = Gluten::getProgramTime();
      }


      //! \brief Stop timing and update timer values.
      //! \return    timed period length (ms)
      inline uintmax_t stop()
      {
         return (time = Gluten::getProgramTime() - mark);
      }
   };


   //! \struct PerfTimer
   //! \brief Performance timer that times a period and tracks the minimum,
   //! \brief maximum, and simple running average of all periods.
   struct PerfTimer : public Timer
   {
      uintmax_t   minimum;
      uintmax_t   maximum;
      uintmax_t   average;
      float       ratio;


      //! \brief Parametric constructor.
      //! \param[in]    ratio          ratio of average from new data [0, 1]
      PerfTimer(float ratio) :
         Timer(),
         minimum(std::numeric_limits<uintmax_t>::max()),
         maximum(0),
         average(0),
         ratio(ratio)
      {
      }


      //! \brief Reset timer values.
      void reset()
      {
         mark = 0;
         time = 0;
         minimum = std::numeric_limits<uintmax_t>::max();
         maximum = 0;
         average = 0;
         averagePrev = 0;
      }


      //! \brief Stop timing and update timer values.
      //! \return       timed period length (ms)
      inline uintmax_t stop()
      {
         time = Gluten::getProgramTime() - mark;

         minimum = std::min(minimum, time);
         maximum = std::max(maximum, time);

         averagePrev = average;
         average = static_cast<uintmax_t>(
            time * ratio + averagePrev * (1.0f - ratio));

         return time;
      }


   private:
      uintmax_t   averagePrev;
   };
}

#endif
